
use std::str::FromStr;

use super::app_with_site_config::get_app_with_site_config_vec;
use crate::{config::app_yml::{app_files::AppFiles, log_item::LogItem, version_info::VersionInfo, AppLog}, util::{app_with_site_config::AppWithSiteConfig, date_time_helper::get_naive_date, log_helper::{push_dev, push_error, push_info}, path_helper::get_is_exists, site_directory::{make_cache_site_directory_all, make_site_directory_all}, yaml_helper::{read_yaml_file, write_yaml_file}}};


pub async fn start_update_history_log() {
  make_site_directory_all();
  let config_vec = get_app_with_site_config_vec();
  let future_vec = config_vec.iter().map(|app_config| {
    return write_app_history_log(app_config);
  });
  futures::future::join_all(future_vec).await;
}

pub async fn write_app_history_log(app_config: &AppWithSiteConfig) {
  let yaml_path = app_config.get_app_yml_path();
  let yaml_path_str = yaml_path.as_str();
  let exist_result = get_is_exists(yaml_path_str);
  if exist_result.is_not_check() {
    return ;
  }
  let app_files_info = get_app_files_info(app_config).await;
  if app_files_info.is_none() {
    return ;
  }
  let version_info = get_app_version_info(app_config).await;
  let app_files = app_files_info.unwrap();
  let mut new_log = LogItem::simple_new(version_info, app_files);
  let new_log_string = serde_yml::to_string(&new_log).unwrap_or(String::new());
  let new_log_str = new_log_string.as_str();
  if exist_result.is_not_exist() {
    push_info(format!("start add log {}\n{}", yaml_path_str, new_log_str));
    let mut app_log = AppLog::new(app_config.app_config.get_server_name());
    new_log.update_remark_content(String::from("第一次抓取，结果仅供参考"));
    app_log.add_log(new_log);    
    write_yaml_file(yaml_path_str, &app_log);
    return ;
  }
  let mut app_log = read_yaml_file::<AppLog>(yaml_path_str);
  let last_log = app_log.get_last_log();
  if last_log.is_some() {
    let last_log = last_log.unwrap();
    if last_log.eq(&new_log) {
      push_info(format!("log is same, skip update log {}\n{}", yaml_path_str, new_log_str));
      return ;
    }
  }
  push_info(format!("start update log {}\n{}", yaml_path_str, new_log_str));
  app_log.add_log(new_log);
  write_yaml_file(yaml_path_str, &app_log);
}

pub async fn get_app_version_info(app_config: &AppWithSiteConfig) -> Option<VersionInfo> {
  let url_version = app_config.get_app_url_version();
  let response_text_result = request_url_text(url_version.as_str()).await;
  if response_text_result.is_none() {
    return None;
  }
  let response_text = response_text_result.unwrap();
  let version_info_result = VersionInfo::from_json_str(response_text.as_str());
  return version_info_result;
}

pub async fn get_app_files_info(app_config: &AppWithSiteConfig) -> Option<AppFiles> {
  let url_index = app_config.get_app_url_index();
  let response_text_result = request_url_text(url_index.as_str()).await;
  if response_text_result.is_none() {
    return None;
  }
  let response_text = response_text_result.unwrap();
  return AppFiles::from_html(response_text.as_str());
}

pub async fn request_url_text(url_str: &str) -> Option<String> {
  let request = reqwest::get(url_str).await;
  if request.is_ok() {
    let response = request.unwrap();
    if response.status() != 200 {
      push_error(format!("request error on response with status[{}], url: {}", response.status(), url_str));
      return None;
    }
    let version_info_text_result = response.text().await;
    if version_info_text_result.is_err() {
      push_error(format!("request error on response text, url: {}", url_str));
      return None;
    }
    let version_info_text = version_info_text_result.unwrap();
    return Some(version_info_text);
  }
  push_error(format!("request error on request, url: {}", url_str));
  return None;
}


pub async fn start_read_log(
  after_date_option: Option<chrono::NaiveDate>,
  before_date_option: Option<chrono::NaiveDate>,
) {
  make_cache_site_directory_all();
  let config_vec = get_app_with_site_config_vec();
  let future_vec = config_vec.iter().map(|app_config| {
    return read_app_history_log(app_config, after_date_option, before_date_option);
  });
  futures::future::join_all(future_vec).await;
}

pub async fn read_app_history_log(
  app_config: &AppWithSiteConfig,
  after_date_option: Option<chrono::NaiveDate>,
  before_date_option: Option<chrono::NaiveDate>
) {
  let yaml_path = app_config.get_app_yml_path();
  let yaml_path_str = yaml_path.as_str();
  let yaml_file_exists = get_is_exists(yaml_path_str);
  if yaml_file_exists.is_not_check() {
    return;
  }
  if yaml_file_exists.is_not_exist() {
    return;
  }
  let app_log = read_yaml_file::<AppLog>(yaml_path_str);
  let target_vec = get_log_vec_between(app_log.get_list(), after_date_option, before_date_option);
  let cache_yaml_path = app_config.get_app_cache_log_yml_path();
  let cache_yaml_path_str = cache_yaml_path.as_str();
  let cache_yaml_path_buf_result = std::path::PathBuf::from_str(cache_yaml_path_str);
  if cache_yaml_path_buf_result.is_err() {
    push_error(format!("cache_yaml_path_str cannot to be path_buf: {}", cache_yaml_path_str));
    return;
  }
  let cache_yaml_path_buf = cache_yaml_path_buf_result.unwrap();
  let cache_yaml_dir_path_option = cache_yaml_path_buf.parent();
  if cache_yaml_dir_path_option.is_none() {
    push_error(format!("cannot get dirname of : {}", cache_yaml_path_str));
    return;
  }
  let cache_yaml_dir_path = cache_yaml_dir_path_option.unwrap();
  if !cache_yaml_dir_path.exists() {
    let cache_yaml_dir_path_str_option = cache_yaml_dir_path.to_str();
    if cache_yaml_dir_path_str_option.is_none() {
      push_error(format!("cannot get dirname str of : {}", cache_yaml_path_str));
      return;
    }
    let cache_yaml_dir_path_str = cache_yaml_dir_path_str_option.unwrap();
    let create_result = std::fs::create_dir_all(cache_yaml_dir_path_str);
    if create_result.is_err() {
      push_error(format!("cannot create dirname str of : {} with error: {}", cache_yaml_path_str, create_result.err().unwrap()));
      return;
    }
  }
  if target_vec.len() > 0 {
    write_yaml_file(cache_yaml_path_str, &target_vec);
  }
  push_dev(
    format!(
      "{} result is {:#?}", 
      app_config.get_union_name(),
      target_vec
    )
  );
}


pub fn get_log_vec_between(
  log_vec: &Vec<LogItem>,
  after_date_option: Option<chrono::NaiveDate>,
  before_date_option: Option<chrono::NaiveDate>
) -> Vec<LogItem> {
  let mut result_vec: Vec<LogItem> = Vec::new();
  for log_one in log_vec {
    let log_date_str = log_one.log_date.as_str();
    let log_date_result = get_naive_date(log_date_str);
    if log_date_result.is_none() {
      continue;
    }
    let log_date = log_date_result.unwrap();
    if after_date_option.is_some() {
      let after_date = after_date_option.unwrap();
      if !log_date.ge(&after_date) {
        break;
      }
    }
    if before_date_option.is_some() {
      let before_date = before_date_option.unwrap();
      if !log_date.le(&before_date) {
        continue;
      }
    }
    result_vec.push(log_one.clone());
  }
  return result_vec;
}
